'''
Created on 07/gen/2010

@author: syn
'''

import gtk

from ui import *
from soap import *

from ui.GladeBase import GladeBase
from ui.AddEdition import AddEdition
from ui.AddPrint import AddPrint
from utils import string_hook, logger

NO_ENTITY = -1

class ManageProduct(GladeBase):
    def __init__(self, query=None, top_component=None):
        GladeBase.__init__(self, 'manage_product_frame', top_component)
        
        # Setting up product list
        cell = gtk.CellRendererText()
        tvc = gtk.TreeViewColumn('Product')
        tvc.pack_start(cell, True)
        tvc.set_attributes(cell,text=1)
        self.product_treeview.append_column(tvc)
        
        self.product_list = gtk.ListStore(int, str, object)
        self.product_list.set_sort_column_id(1, gtk.SORT_ASCENDING)
        
        try:
            products = client.get_products()
            for product in products:
                self._append_product(product)
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
        
        self.product_treeview.set_model(self.product_list)
        self.product_frame_hpaned.set_position(400)
        
        self._load_book_list()
        self._load_edition_list()
        
        self._init_gui_components()
        
        self.entity_key = NO_ENTITY
    
    def _append_product(self, product):
        """
        Add an product to the list
        """
        self.product_list.append([product.id, product.name, product])
        
    def _update_product(self, row, product):
        """
        Update an product row in model with new value
        """
        self.product_list.set(row.iter, 1, product.name, 2, product)
    
    def _init_gui_components(self):
        """
        Intialize gui components as needed. This is useful in the contructor or 
        binded with particular actions (for instance, a "clear" button, or "new" button) 
        """
        self.name_entry.set_text("")
        self.description_textbuffer.set_text("")
        self.price_entry.set_text("")
        self.discount_spinbutton.set_value(0)
        self.edition_combobox.set_active(-1)
        self.book_combobox.set_active(-1)
        self.availability_text.set_text("0")
        
        iter = self.bookproduct_list.get_iter_first()
        while iter:
            self.bookproduct_list.remove(iter)
            iter = self.bookproduct_list.get_iter_first()
        
        self.apply_button.set_sensitive(False)
        
    def _retrieve_components(self):
        """
        Retrieve all used gui components from builder. This method is called in the
        GladeBase constructor
        """
        self.get_from_builder('apply_button')
        self.get_from_builder('product_treeview')
        self.get_from_builder('name_entry')
        self.get_from_builder('price_entry')
        self.get_from_builder('discount_spinbutton')
        self.get_from_builder('edition_combobox')
        self.get_from_builder('description_textbuffer')
        self.get_from_builder('book_combobox')
        self.get_from_builder('book_treeview')
        self.get_from_builder('bookproduct_list')
        self.get_from_builder('availability_text')
        self.get_from_builder('product_frame_hpaned')
    
    def _load_book_list(self):
        """ Load all books in database into the combobox """
        book_list = gtk.ListStore(int, str)
        try:
            books = client.get_books()
            for book in books:
                book_list.append([book.id, book.title])
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
            
        self.book_combobox.set_model(book_list)

    def _load_edition_list(self):
        """ Load all editions in database into the combobox """
        edition_list = gtk.ListStore(int, str)
        try:
            editions = client.get_editions()
            for edition in editions:
                edition_list.append([edition.id, edition.name])
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
        
        self.edition_combobox.set_model(edition_list)

    def _has_valid_data(self):
        """
        Formal control for data in the input form.
        @return: boolean. 'True' when data is valid
        """
        name = self.name_entry.get_text()
        if not name:
            mandatory_data_error('Name', self.name_entry)
            return False
        
        price = self.price_entry.get_text()
        if not price:
            mandatory_data_error('Price', self.price_entry)
            return False
        
        try:
            price = float(price)
            self.price_entry.set_text(str(round(price, 2)))
        except ValueError:
            generic_data_error("Price value is not numeric", self.price_entry)
            return False
        
        iter = self.bookproduct_list.get_iter_first()
        if not iter:
            generic_data_error("Insert at least a book for the product", self.book_combobox)
            return False
        
        return True
            
    def _save_data(self):
        """
        Save changes to db. 
        @return: 'True' if data have been successfully saved 
        """
        if not self._has_valid_data():
            return False
        
        # manage Product data object
        if self.entity_key == NO_ENTITY:
            product = ProductSoap()
            product.quantity = 0
        else:
            row = search(self.product_list, (0, self.entity_key))
            product = row[2]
        
        product.name = self.name_entry.get_text()
        (start,end) = self.description_textbuffer.get_bounds()
        product.description = string_hook(self.description_textbuffer.get_text(start,end).strip())
        
        iter = self.edition_combobox.get_active_iter()
        if iter:
            product.edition = self.edition_combobox.get_model().get_value(iter, 0)
        
        product.price = float(self.price_entry.get_text())
        product.discount = self.discount_spinbutton.get_value_as_int() 
        
        product.books = []
        for (book_num, book_title, book_id) in self.bookproduct_list:
            pb = ProductBookSoap()
            pb.sequence_num = book_num
            pb.book = book_id
            pb.title = book_title
            product.books.append(pb)
        
        # save changes
        try:
            product.id = client.save_product(product)
            if not product.id > 0:
                raise Exception("Problem occurred on server during save")
            
            if self.entity_key == NO_ENTITY:
                self._append_product(product)
            else:
                # update row in list
                row = search(self.product_list, (0, product.id))
                self._update_product(row, product)
            
            self.entity_key = product.id
            self.apply_button.set_sensitive(False)
            return True
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
            return False
    
    # Signals
    def on_new_button_clicked(self, object, data=None):
        if self.apply_button.get_property('sensitive'):
            res = get_save_dialog(self.get_top_component(), "Save changes?")
            if res == gtk.RESPONSE_OK:
                if not self._save_data():
                    return
                
        self._init_gui_components()
        self.product_treeview.get_selection().unselect_all()
        self.entity_key = NO_ENTITY
    
    def on_apply_button_clicked(self, object, data=None):
        self._save_data()
    
    def on_delete_button_clicked(self, object, data=None):
        (model, iter) = self.product_treeview.get_selection().get_selected()
        if not iter:
            return
        
        product = model.get_value(iter, 2)
        if product.orders:
            get_info_dialog(self.get_top_component(), 
                            "There are orders for this product, can't be deleted.\n" \
                            "Check and remove orders first.")
            return
        
        try:
            client.delete_product(product)
            model.remove(iter)
            
            self._init_gui_components()
            self.entity_key = NO_ENTITY
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)

    def on_product_changed(self, *args, **kwargs):
        self.apply_button.set_sensitive(True)
        
    def on_new_edition_button_clicked(self, object, data=None):
        dialog = AddEdition(self.get_top_component())
        while True:
            response = dialog.run()
            if response: break
        dialog.destroy()
        
        if response == gtk.RESPONSE_OK:
            self._load_edition_list()
            
    def on_add_book_button_clicked(self, object, data=None):
        iter = self.book_combobox.get_active_iter()
        if not iter:
            return
        
        book_id = self.book_combobox.get_model().get_value(iter, 0)
        book_title = self.book_combobox.get_model().get_value(iter, 1)
        model = self.book_treeview.get_model()
        # This is needed to avoid duplication
        if search(model, (2, book_id)):
            return
        
        num = len(model) + 1 # New row number, starting from 1
        row = [num, book_title, book_id]
        self.bookproduct_list.append(row)
        
        # Autocomplete product name
        if not self.name_entry.get_text().strip():
            self.name_entry.set_text("%s" % (book_title))
        
    def on_product_treeview_row_activated(self, treeview, path, view_column, data=None):
        if self.apply_button.get_property('sensitive'):
            res = get_save_dialog(self.get_top_component(), "Save changes?")
            if res == gtk.RESPONSE_OK:
                if not self._save_data():
                    return
                    
        self._init_gui_components()
        
        product = self.product_list[path][2]
        self.entity_key = product.id
        
        self.name_entry.set_text(product.name)
        self.description_textbuffer.set_text(string_hook(product.description))
        
        if product.edition:
            row = search(self.edition_combobox.get_model(), (0, product.edition))
            self.edition_combobox.set_active_iter(row.iter)
        
        self.price_entry.set_text(str(product.price))
        self.discount_spinbutton.set_value(product.discount)
        
        self.availability_text.set_text(str(product.quantity))
        
        for pb in product.books:
            row = [pb.sequence_num, pb.title, pb.book]
            self.bookproduct_list.append(row)
        
        self.apply_button.set_sensitive(False)
    
    def on_up_button_clicked(self, object, data=None):
        """ Move one row from the treeview, up in the list """
        (model, iter) = self.book_treeview.get_selection().get_selected()
        if not iter: return
        
        path = model.get_path(iter)
        # if the first row is selected we can't move up
        if path == (0,):
            return
        
        # get previous iter and update sequence number
        prev_iter = model.get_iter((path[0]-1,))
        seq_num = model.get_value(prev_iter, 0)
        model.set_value(prev_iter, 0, seq_num + 1)
        model.set_value(iter, 0, seq_num)
        model.move_before(iter, prev_iter)
    
    def on_down_button_clicked(self, object, data=None):
        """ Move one row from the treeview, down in the list """
        (model, iter) = self.book_treeview.get_selection().get_selected()
        if not iter: return
        
        path = model.get_path(iter)
        # if last row is selected we can't move down
        if path == (len(model)-1,):
            return
        
        # get next iter, update sequence number and move rows
        next_iter = model.get_iter((path[0]+1,))
        seq_num = model.get_value(next_iter, 0)
        model.set_value(next_iter, 0, seq_num - 1)
        model.set_value(iter, 0, seq_num)
        model.move_after(iter, next_iter)
        
    def on_remove_button_clicked(self, object, data=None):
        """ Delete the selected row from the book treeview """
        (model, iter) = self.book_treeview.get_selection().get_selected()
        if not iter: return
        
        i = model.iter_next(iter)
        while i:
            seq_num = model.get_value(i, 0)
            model.set_value(i, 0, seq_num - 1)
            i = model.iter_next(i)
        
        model.remove(iter)
        
    def on_print_button_clicked(self, object, data=None):
        if self.entity_key == NO_ENTITY:
            generic_data_error("Save or select a product before send it to print", self.apply_button)
            return
        
        try:
            print_queued = client.get_prints(self.entity_key)
        except Exception as e:
            get_error_dialog(self.get_top_component(), e)
            return
            
        if print_queued:
            generic_data_error("There is a pending print for this product", self.name_entry)
            return
        
        print_dialog = AddPrint(self.get_top_component(), self.entity_key)
        while True:
            response = print_dialog.run()
            if response: break
        print_dialog.destroy()
        
        if response == gtk.RESPONSE_OK:
            get_info_dialog(self.get_top_component(), "Print has been sent")
        